﻿using System.Collections.Generic;

namespace Interpreter
{
    namespace Inner
    {
        namespace CodeGenerator
        {
            /// <summary>
            /// Abstract Parser tress node , can be 
            /// Expression(Push a value onto a stack)
            /// leftVals (The stack remain unchanged, but require a value already pushed onto stack)
            /// Statement (The stack remain unchanged)
            /// </summary>
            abstract class ParseTreeNode
            {
                /// <summary>
                /// Fill the instruction into the instruction buffer
                /// </summary>
                /// <param name="buffer">Instruction buffer</param>
                /// <param name="startPos">Start of the bytecode position</param>
                /// <returns>Generated code length</returns>
                public abstract int getInstruction(Instruction[] buffer, int startPos);
                public ParseTreeNode(Machine m, int line)
                {
                    _m = m;
                    _line = line;
                }
                /// <summary>
                /// Throw a Compile Exception
                /// </summary>
                /// <param name="description">Description</param>
                protected void ThrowException(string description)
                {
                    throw new CompileException(_line, description);
                }

                /// <summary>
                /// Set the symbol table
                /// </summary>
                /// <param name="startPos">startPos</param>
                protected void SymbolSetCodeStartPos(int startPos)
                {
                    _m.parser_setLineCode(_line, startPos);
                }

                protected Machine _m;
                protected int _line;
            }

            /// <summary>
            /// Statement (Do not alter the stack after executing)
            /// </summary>
            abstract class Statement
                :ParseTreeNode
            {
                public Statement(Machine m, int line)
                    :base (m,line)
                {
                }
            }

            /// <summary>
            /// Expression/leftVals
            /// Expression(.left() not called) (Push a value onto the stack)
            /// leftVals(.left() called) (The stack remain unchanged, but require a value already pushed onto stack)
            /// </summary>
            abstract class Expression
                : ParseTreeNode
            {
                /// <summary>
                /// A expression cannot be a leftVal unless overrided
                /// </summary>
                public virtual void left()
                {
                    ThrowException("Cannot be a left value.");
                }

                public Expression(Machine m, int line)
                    : base(m,line)
                {
                }
            }

            #region Expressions
            /// <summary>
            /// Compare Two Expressions
            /// </summary>
            class exp_compare
                : Expression
            {
                private Expression _left;
                private Expression _right;
                private int _compareMode;

                public exp_compare(Machine m, int line, Expression left, Expression right, int compareMode)
                    : base(m,line)
                {
                    _left = left;
                    _right = right;
                    _compareMode = compareMode;
                }
                
                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    // Generated Instructions:
                    // <_left>  ;PUSH Left Value to the stack
                    // <_right> ;PUSH Right Value to the stack
                    // CMP compareMode ;Perform Comapre
                    Instruction ins; int pos = 0;
                    pos += _left.getInstruction(buffer, startPos + pos);
                    pos += _right.getInstruction(buffer, startPos + pos);
                    ins.opCode = OpCode.CMP;
                    ins.pointer = _compareMode;
                    ins.num = 0;
                    buffer[startPos + pos] = ins;
                    pos++;
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// Invert a expression
            /// </summary>
            class exp_not
                : Expression
            {
                private Expression _exp;

                public exp_not(Machine m, int line, Expression exp)
                    : base(m, line)
                {
                    _exp = exp;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    // Generated Instructions:
                    // <_exp>   ;Push the expression onto the stack
                    // NOT      ;Perfrom Invertion
                    Instruction ins; int pos = 0;
                    pos += _exp.getInstruction(buffer, startPos + pos);
                    ins.opCode = OpCode.NOT;
                    ins.pointer = 0;
                    ins.num = 0;
                    buffer[startPos + pos] = ins;
                    pos++;
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// Perform logical AND calculation
            /// </summary>
            class exp_and
                : Expression
            {
                private Expression _left;
                private Expression _right;

                public exp_and(Machine m, int line, Expression left, Expression right)
                    : base(m, line)
                {
                    _left = left;
                    _right = right;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    // Generated Instructions:
                    // <_left>              ;Push the left expression onto the stack
                    // JUMP testRight       ;Test the right expression if the left expression is non-zero
                    // PUSHNUM 0            ;The left expression is zero, so push the result 0
                    // JUMPA testEnd        ;Do not need to compare the right expression
                    // :testRight <_right>  ;Push the right expression onto the stack
                    // :testEnd
                    Instruction ins; int pos = 0;
                    pos += _left.getInstruction(buffer, startPos + pos);

                    ins.opCode = OpCode.JUMP;
                    ins.pointer = 3;
                    ins.num = 0;
                    buffer[startPos + pos] = ins;
                    pos++;

                    ins.opCode = OpCode.PUSHNUM;
                    buffer[startPos + pos] = ins;
                    pos++;

                    ins.opCode = OpCode.JUMPA;
                    ins.pointer = _right.getInstruction(buffer, startPos + pos + 1) + 1;
                    buffer[startPos + pos] = ins;
                    pos++;

                    pos += (ins.pointer - 1);

                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// Perform OR Operation
            /// Uses exp_not and exp_and
            /// </summary>
            class exp_or
                : Expression
            {
                private Expression _left;
                private Expression _right;

                public exp_or(Machine m, int line, Expression left, Expression right)
                    : base(m, line)
                {
                    _left = left;
                    _right = right;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    Expression exp = new exp_not(_m, _line,
                        new exp_and(_m, _line, new exp_not(_m, _line, _left), new exp_not(_m, _line, _right))
                    );
                    int pos = exp.getInstruction(buffer, startPos);
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// A Number Expression
            /// </summary>
            class exp_number
                : Expression
            {
                private double _num;

                public exp_number(Machine m, int line, double num)
                    : base(m, line)
                {
                    _num = num;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    // Generated Instructions:
                    // PUSHNUM num  ;Push the number onto the stack
                    Instruction ins; int pos = 0;
                    ins.opCode = OpCode.PUSHNUM;
                    ins.pointer = 0;
                    ins.num = _num;
                    buffer[startPos + pos] = ins;
                    pos++;
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// A String Expression
            /// </summary>
            class exp_string
                : Expression
            {
                private int _str;

                public exp_string(Machine m, int line, int str)
                    : base(m, line)
                {
                    _str = str;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    // Generated Instructions:
                    // PUSHNUM str  ;Push the string onto the stack
                    Instruction ins; int pos = 0;
                    ins.opCode = OpCode.PUSHSTRING;
                    ins.pointer = _str;
                    ins.num = 0;
                    buffer[startPos + pos] = ins;
                    pos++;
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// Global Variable
            /// Can be leftVal
            /// </summary>
            class exp_var
                : Expression
            {
                private int _var;
                private bool _left;

                public exp_var(Machine m, int line, int var)
                    : base(m, line)
                {
                    _var = var;
                    _left = false;
                }

                /// <summary>
                /// Trensform the expression to be a leftVal.
                /// </summary>
                public override void left()
                {
                    _left = true;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    // Generated Instructions:
                    // GETGLOBAL id     ;Push the global onto the stack
                    // Generated Instructions: (leftVal)
                    // SETGLOBAL id     ;Set the global to be the top of the stack
                    Instruction ins; int pos = 0;
                    if (_left)
                        ins.opCode = OpCode.SETGLOBAL;
                    else
                        ins.opCode = OpCode.GETGLOBAL;
                    ins.pointer = _var;
                    ins.num = 0;
                    buffer[startPos + pos] = ins;
                    pos++;
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// Local Variable
            /// Can be leftVal
            /// </summary>
            class exp_local_var
                : Expression
            {
                private int _var;
                private bool _left;

                public exp_local_var(Machine m, int line, int var)
                    : base(m, line)
                {
                    _var = var;
                    _left = false;
                }

                public override void left()
                {
                    _left = true;
                }


                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    // Generated Instructions:
                    // GETLOCAL id     ;Push the local onto the stack
                    // Generated Instructions: (leftVal)
                    // SETLOCAL id     ;Set the local to be the top of the stack
                    Instruction ins; int pos = 0;
                    if (_left)
                        ins.opCode = OpCode.SETLOCAL;
                    else
                        ins.opCode = OpCode.GETLOCAL;
                    ins.pointer = _var;
                    ins.num = 0;
                    buffer[startPos + pos] = ins;
                    pos++;
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// A new table (with initializations)
            /// </summary>
            class exp_new_table
                : Expression
            {
                public exp_new_table(Machine m, int line)
                    : base(m, line)
                {
                    tableInitList = null;
                }

                /// <summary>
                /// Add a table Initialization Statement
                /// </summary>
                /// <param name="stmt">Table Initialization statement</param>
                public void AddInitStmt(Statement stmt)
                {
                    if (tableInitList == null)
                        tableInitList = new List<Statement>();
                    tableInitList.Add(stmt);
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    // Generated Instructions:
                    // TABLEOP TABLE_NEW        ;Create a new table
                    // <Init statement 1>       ;Init statement 1
                    // ...                      ;...
                    // <Init statement n>       ;Init statement n
                    Instruction ins; int pos = 0;
                    ins.opCode = OpCode.TABLEOP;
                    ins.pointer = OpCode.TABLE_NEW;
                    ins.num = 0;
                    buffer[startPos + pos] = ins;
                    pos++;
                    if (tableInitList != null)
                    {
                        foreach (var stmt in tableInitList)
                            pos += stmt.getInstruction(buffer, startPos + pos);
                    }
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }

                private List<Statement> tableInitList;
            }

            /// <summary>
            /// A table item
            /// Can be leftVal
            /// </summary>
            class exp_table_item
                : Expression
            {
                private Expression _table;
                private Expression _index;
                private bool _left;

                public exp_table_item(Machine m, int line, Expression table, Expression index)
                    : base(m, line)
                {
                    _table = table;
                    _index = index;
                    _left = false;
                }

                public override void left()
                {
                    _left = true;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    // Generated Instructions:
                    // <_table>                 ;Push the table onto the stack
                    // <_index>                 ;Push the key onto the stack
                    // TABLEOP TABLE_GETITEM    ;Get the table item
                    // Generated Instructions: (letVal)
                    // <_table>                 ;Push the table onto the stack
                    // <_index>                 ;Push the key onto the stack
                    // TABLEOP TABLE_SETITEM    ;Set the table item
                    Instruction ins; int pos = 0;
                    pos += _table.getInstruction(buffer, startPos + pos);
                    pos += _index.getInstruction(buffer, startPos + pos);

                    ins.opCode = OpCode.TABLEOP;
                    if (_left)
                        ins.pointer = OpCode.TABLE_SETITEM;
                    else
                        ins.pointer = OpCode.TABLE_GETITEM;
                    ins.num = 0;
                    buffer[startPos + pos] = ins;
                    pos++;
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// A table initialization statement
            /// </summary>
            class exp_table_item_init
                : Expression
            {
                private Expression _index;
                private bool _left;

                public exp_table_item_init(Machine m, int line, Expression index)
                    : base(m, line)
                {
                    _index = index;
                    _left = false;
                }

                public override void left()
                {
                    _left = true;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    // Generated Instructions:
                    // <_index>                     ;Push the key onto the stack
                    // TABLEOP TABLE_SETITEMINIT    ;Set the table item
                    Instruction ins; int pos = 0;
                    pos += _index.getInstruction(buffer, startPos + pos);

                    ins.opCode = OpCode.TABLEOP;
                    if (!_left)
                        ThrowException("Unexpected Table Initialize"); //Should never reach here
                    ins.pointer = OpCode.TABLE_SETITEM_INIT;
                    ins.num = 0;
                    buffer[startPos + pos] = ins;
                    pos++;
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// Assign a expression(must be a leftVal) to be another expression
            /// But keep the right value on the stack
            /// </summary>
            class exp_assignment
                : Expression
            {
                private Expression _exp;
                private Expression _left;

                public exp_assignment(Machine m, int line, Expression left, Expression exp)
                    : base(m, line)
                {
                    _left = left;
                    _exp = exp;
                    _left.left();
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    //Generated Instructions:
                    //  <_exp>      ;Push the right value onto the stack
                    //  <_left>     ;Assign the value to the leftVal (which uses the stack top but not change it)
                    int pos = 0;
                    pos += _exp.getInstruction(buffer, startPos + pos);
                    pos += _left.getInstruction(buffer, startPos + pos);
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// Perform arithmatic operation
            /// </summary>
            class exp_arith
                : Expression
            {
                private Expression _left;
                private Expression _right;
                private int _arithType;

                public exp_arith(Machine m, int line, Expression left, Expression right, int arithType)
                    : base(m, line)
                {
                    _left = left;
                    _right = right;
                    _arithType = arithType;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    // Generated Instructions:
                    // <_left>                     ;Push the left value onto the stack
                    // <_right>                    ;Push the right value onto the stack
                    // ARITH _arithType            ;Perform arithmatic operation
                    Instruction ins; int pos = 0;
                    pos += _left.getInstruction(buffer, startPos + pos);
                    pos += _right.getInstruction(buffer, startPos + pos);

                    ins.opCode = OpCode.ARITH;
                    ins.pointer = _arithType;
                    ins.num = 0;
                    buffer[startPos + pos] = ins;
                    pos++;

                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// A function call
            /// </summary>
            class exp_functioncall
                : Expression
            {
                private List<Expression> _exprList;
                private Expression _func;

                public exp_functioncall(Machine m, int line, List<Expression> exprList, Expression func)
                    : base(m, line)
                {
                    _exprList = exprList;
                    _func = func;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    // Generated Instructions:
                    // <_exprList[0]>              ;Push the first param onto the stack
                    // <_exprList[1]>              ;Push the second param onto the stack
                    // ...
                    // <_exprList[n]>              ;Push the last param onto the stack
                    // <_func>                     ;Push the call address onto the stack
                    // CALL n                      ;Call the function

                    Instruction ins; int pos = 0;

                    for (int i = 0; i < _exprList.Count; ++i)
                    {
                        pos += _exprList[i].getInstruction(buffer, startPos + pos);
                    }


                    pos += _func.getInstruction(buffer, startPos + pos);

                    ins.opCode = OpCode.CALL;
                    ins.pointer = _exprList.Count;
                    ins.num = 0;
                    buffer[startPos + pos] = ins;
                    pos++;

                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            #endregion

            #region Statements

            /// <summary>
            /// Perform no operation
            /// </summary>
            class stat_nop
                : Statement
            {
                public stat_nop(Machine m, int line)
                    : base(m, line)
                {
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    //Do not generate any instruction
                    SymbolSetCodeStartPos(startPos);
                    return 0;
                }
            }

            /// <summary>
            /// A function body
            /// </summary>
            class stat_function
                : Statement
            {
                private Statement _body;
                private int _numparam;

                public stat_function(Machine m, int line, Statement body, int numparam)
                    : base(m, line)
                {
                    _body = body;
                    _numparam = numparam;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    //Generated instructions:
                    //  ADJLOCAL localCount     ;Adjust stack for locals/params
                    //  <_funcBody>             ;The function body
                    //  PUSHNUM 0               ;Default return value 0
                    //  RET                     ;Default return
                    int pos = 0; Instruction ins;
                    ins.pointer = _numparam; ins.num = 0;
                    ins.opCode = OpCode.ADJLOCAL;
                    buffer[startPos + pos] = ins;
                    pos++;

                    pos += _body.getInstruction(buffer, startPos + pos);
                    
                    ins.pointer = 0; ins.num = 0;
                    ins.opCode = OpCode.PUSHNUM;
                    buffer[startPos + pos] = ins;
                    pos++;
                    
                    ins.pointer = 0; ins.num = 0;
                    ins.opCode = OpCode.RET;
                    buffer[startPos + pos] = ins;
                    pos++;
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }

            }

            /// <summary>
            /// Convert an expression to a statement (discarding the value)
            /// </summary>
            class stat_exp
                : Statement
            {
                //Generated instructions:
                //  <_exp>     ;The expression
                //  POP 1      ;Discard the value (keep stack balance)
                private Expression _exp;

                public stat_exp(Machine m, int line, Expression exp)
                    : base(m, line)
                {
                    _exp = exp;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    int pos = 0; Instruction ins;
                    pos += _exp.getInstruction(buffer, startPos + pos);

                    ins.pointer = 1; ins.num = 0;
                    ins.opCode = OpCode.POP;
                    buffer[startPos + pos] = ins;
                    pos++;
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// Sequential statements
            /// </summary>
            class stat_seq
                : Statement
            {
                private List<Statement> _statList;

                public stat_seq(Machine m, int line, List<Statement> statList)
                    : base(m, line)
                {
                    _statList = statList;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    //Generated Instructions:
                    //  <_statList[0]>      ;The first statement
                    //  <_statList[1]>      ;The second statement
                    //  ....
                    //  <_statList[n]>      ;The last statement

                    int pos = 0;
                    for (int i = 0; i < _statList.Count; ++i)
                    {
                        pos += _statList[i].getInstruction(buffer, startPos + pos);
                    }

                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// Return a value
            /// </summary>
            class stat_return
                : Statement
            {
                private Expression _exp;

                public stat_return(Machine m, int line, Expression exp)
                    : base(m, line)
                {
                    _exp = exp;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    //Generated Instructions:
                    //  <_exp>      ;Push the return value onto the stack
                    //  RET         ;Return
                    int pos = 0; Instruction ins;
                    pos += _exp.getInstruction(buffer, startPos + pos);

                    ins.pointer = 0; ins.num = 0;
                    ins.opCode = OpCode.RET;
                    buffer[startPos + pos] = ins;
                    pos++;
                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            
            /// <summary>
            /// A If statement
            /// </summary>
            class stat_if
                : Statement
            {
                private Expression _exp;
                private Statement _doIfTrue;
                private Statement _doIfFalse;

                public stat_if(Machine m, int line, Expression exp, Statement doIfTrue, Statement doIfFalse)
                    : base(m, line)
                {
                    _exp = exp;
                    _doIfFalse = doIfFalse;
                    _doIfTrue = doIfTrue;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    //Generated Instructions:
                    //  <_exp>                  ;Push the condition value onto the stack
                    //  JUMP do_if_true         ;Jump if true (always pop the condition value)
                    //  <doIfFalse>             ;Do "condition==zero" actions
                    //  JUMPA if_end            ;Skip do_if_true actions
                    //  do_if_true:  <doIfTrue> ;Do "condition!=zero" actions
                    //  if_end:                 ;End of if
                    Instruction ins; int pos = 0;
                    pos += _exp.getInstruction(buffer, startPos + pos);

                    int action_if_true_pos = pos + 1 + _doIfFalse.getInstruction(buffer, startPos + (pos + 1));
                    ins.opCode = OpCode.JUMP;
                    ins.num = 0;
                    ins.pointer = action_if_true_pos - pos + 1;
                    buffer[startPos + pos] = ins;
                    pos++;

                    pos = action_if_true_pos;
                    int end_pos = pos + 1 + _doIfTrue.getInstruction(buffer, startPos + (pos + 1));
                    ins.opCode = OpCode.JUMPA;
                    ins.pointer = end_pos - pos;
                    buffer[startPos + pos] = ins;
                    pos++;

                    pos = end_pos;

                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// A break statement
            /// </summary>
            class stat_break
                : Statement
            {

                public stat_break(Machine m, int line)
                    : base(m, line)
                {
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    //Generated Instruction
                    //PSEUDO_BREAK      ;A JUMPA Instruction address of which must be calculated later
                    int pos = 0; Instruction ins;

                    ins.pointer = 0; ins.num = 0;
                    ins.opCode = OpCode.PSEUDO_BREAK;
                    buffer[startPos + pos] = ins;
                    pos++;

                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// A continue statement
            /// </summary>
            class stat_continue
                : Statement
            {
                public stat_continue(Machine m, int line)
                    : base(m, line)
                {
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    //Generated Instruction
                    //PSEUDO_CONTINUE      ;A JUMPA Instruction address of which must be calculated later
                    int pos = 0; Instruction ins;

                    ins.pointer = 0; ins.num = 0;
                    ins.opCode = OpCode.PSEUDO_CONTINUE;
                    buffer[startPos + pos] = ins;
                    pos++;

                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// A while statement
            /// </summary>
            class stat_while
                : Statement
            {
                private Expression _exp;
                private Statement _body;

                public stat_while(Machine m, int line, Expression exp, Statement body)
                    : base(m, line)
                {
                    _exp = new exp_not(m, line, exp);
                    _body = body;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    //Generated Instructions:
                    //  :while_start <_exp>     ;Push the condition value onto stack, continue jumps here
                    //  NOT                     ;Invert the condition value
                    //  JUMP while_end          ;If the conditon is false, end the while statement
                    //  <_body>                 ;The "while" body
                    //  JUMPA while_start       ;Continue the cycle
                    //  :while_end              ;End of while, break jumps here
                    int pos = 0; Instruction ins;
                    ins.pointer = 0; ins.num = 0;

                    pos += _exp.getInstruction(buffer, startPos + pos);
                    int body_length = _body.getInstruction(buffer, startPos + (pos + 1));
                    //jump to end if false
                    ins.opCode = OpCode.JUMP;
                    ins.pointer = body_length + 2;
                    buffer[startPos + pos] = ins;
                    pos++;
                    pos += body_length;

                    ins.opCode = OpCode.JUMPA;
                    ins.pointer = -pos;
                    buffer[startPos + pos] = ins;
                    pos++;

                    //Scan any break and continue instructions and calculate their address
                    for (int i = 0; i < pos; ++i)
                    {
                        if (buffer[startPos + i].opCode == OpCode.PSEUDO_BREAK)
                        {
                            buffer[startPos + i].opCode = OpCode.JUMPA;
                            buffer[startPos + i].pointer = pos - i;
                        }
                        else if (buffer[startPos + i].opCode == OpCode.PSEUDO_CONTINUE)
                        {
                            buffer[startPos + i].opCode = OpCode.JUMPA;
                            buffer[startPos + i].pointer = -i;
                        }
                    }

                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            /// <summary>
            /// A "for" Statement
            /// </summary>
            class stat_for
                : Statement
            {
                private Statement _init;
                private Expression _exp;
                private Statement _step;
                private Statement _body;

                public stat_for(Machine m, int line,Statement init, Expression exp, Statement step,Statement body)
                    : base(m, line)
                {
                    _init = init;
                    _exp = new exp_not(m, line, exp);
                    _step = step;
                    _body = body;
                }

                public override int getInstruction(Instruction[] buffer, int startPos)
                {
                    //Generated Instructions:
                    //  <_init>                 ;Perform the initial actions
                    //  :for_start <_exp>       ;Push the condition value onto stack
                    //  NOT                     ;Invert the condition value
                    //  JUMP for_end            ;If the conditon is false, end the while statement
                    //  <_body>                 ;The "for" body
                    //  for_continue:           ;"continue" jumps here
                    //  <_step>                 ;The "for" step
                    //  JUMPA for_start         ;Continue the cycle
                    //  :for_end                ;End of for, break jumps here
                    int pos = 0; Instruction ins;
                    ins.pointer = 0; ins.num = 0;

                    int init_size = _init.getInstruction(buffer, startPos + pos);
                    pos += init_size;

                    pos += _exp.getInstruction(buffer, startPos + pos);
                    int body_length = _body.getInstruction(buffer, startPos + (pos + 1));
                    int step_length = _step.getInstruction(buffer, startPos + (pos + 1) + body_length);
                    //jump to end if false
                    ins.opCode = OpCode.JUMP;
                    ins.pointer = body_length + step_length + 2;
                    buffer[startPos + pos] = ins;
                    pos++;
                    pos = pos + body_length + step_length;

                    ins.opCode = OpCode.JUMPA;
                    ins.pointer = -(pos - init_size);
                    buffer[startPos + pos] = ins;
                    pos++;

                    //Scan any break and continue instructions and calculate their address
                    for (int i = 0; i < pos - init_size; ++i)
                    {
                        if (buffer[startPos + init_size + i].opCode == OpCode.PSEUDO_BREAK)
                        {
                            buffer[startPos + init_size + i].opCode = OpCode.JUMPA;
                            buffer[startPos + init_size + i].pointer = pos - i - init_size;
                        }
                        else if (buffer[startPos + init_size + i].opCode == OpCode.PSEUDO_CONTINUE)
                        {
                            buffer[startPos + init_size + i].opCode = OpCode.JUMPA;
                            buffer[startPos + init_size + i].pointer = pos - i - init_size - step_length - 1;
                        }
                    }

                    SymbolSetCodeStartPos(startPos);
                    return pos;
                }
            }

            #endregion
        }
    }
}